package tools

import (
	"context"
	"encoding/json"
	"fmt"
	"gitee.com/dsler-go/utils/servers/log"
	"io/ioutil"
	"net/http"
	"runtime/debug"
	"strings"
	"time"
)

const IGNORE_RESULT_LOG_KEY = "__IGNORE_RESULT_LOG__"

type ReturnLocal struct {
	PostData  string        // post 进去的数据
	Url       string        //
	Duration  time.Duration // 请求耗时
	RawResult string        // 服务器返回的原始数据
	Result    interface{}   // 返回结果
	SatusCode int           // 返回的code码
	Err       error         // 错误信息
	TraceId   string
}

// ---------------------------------------
//
//	封装了一层的 http Request 请求
//	application/json
//
// ---------------------------------------
func CommonRequestJson(httpClient *http.Client, url string, method string, params string, result interface{}) (*ReturnLocal, error) {
	return doCommonRequest(3, "application/json", httpClient, url, method, params, result)
}

// -------------------------------------------
//
//	兼容老接口
//
// -------------------------------------------
func doCommonRequest(eofTryTimes int, header string, httpClient *http.Client, url string, method string, params string, result interface{}) (*ReturnLocal, error) {
	return doCommonRequestWithTrace(nil, eofTryTimes, header, httpClient, url, method, params, result)
}

func getNewHttpClient(httpClient *http.Client) *http.Client {
	return httpClient
}

// ---------------------------------------
//
//	封装了一层的 http Request 请求
//	application/x-www-form-urlencoded
//	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
//
// ---------------------------------------
func doCommonRequestWithTrace(ctx context.Context, eofTryTimes int, header string, httpClientOld *http.Client, url string, method string, params string, result interface{}) (*ReturnLocal, error) {

	httpClient := getNewHttpClient(httpClientOld)

	returnLocal := ReturnLocal{SatusCode: -1, PostData: params}
	// 请求数据
	returnLocal.Url = url
	beginTime := time.Now()

	// 如果返回结果过长，可以通过设置该值忽略日志打印
	ignoreResultLog := false
	if ctx != nil {
		ignoreResultLog, _ = ctx.Value(IGNORE_RESULT_LOG_KEY).(bool)
	}

	// 退出时，保证执行了日志打印
	defer doHttpFinish(&returnLocal, ignoreResultLog)

	req, err := http.NewRequest(method, url, strings.NewReader(params))
	if err != nil {
		returnLocal.Err = err
		return &returnLocal, err
	}

	if ctx != nil {
		req = req.WithContext(ctx)
	}

	req.Header.Set("Content-Type", header)
	if err != nil {
		returnLocal.Err = err
		return &returnLocal, err
	}

	res, err := httpClient.Do(req)

	duration := time.Now().Sub(beginTime)
	returnLocal.Duration = duration

	// 如果返回的 res 对象不为空，直接使用 statusCode
	if res != nil {
		returnLocal.SatusCode = res.StatusCode
	}

	// 如果请求返回的结果为 EOF，直接重试
	if err != nil && strings.Contains(err.Error(), "EOF") && eofTryTimes >= 1 {
		log.Error("[third_http_eof_retry] tryTimes:%v url:%v method:%v parms:%v", eofTryTimes, url, method, params)
		return doCommonRequestWithTrace(ctx, eofTryTimes-1, header, httpClient, url, method, params, result)
	}

	if err != nil {
		returnLocal.Err = err
		return &returnLocal, err
	}

	defer res.Body.Close()

	// -----------------------------------------------------------------
	// 读取返回的数据
	// -----------------------------------------------------------------
	body, err := ioutil.ReadAll(res.Body)
	if err != nil {
		returnLocal.Err = err
		return &returnLocal, err
	}
	returnLocal.RawResult = string(body)
	if result != nil {
		err = json.Unmarshal(body, result)
		if err != nil {
			//log.DbErrorWithCtx(ctx, err, "doCommonRequestWithTrace result", "json.Unmarshal error,result:%v", result)
			returnLocal.Err = err
			return &returnLocal, err
		}

		returnLocal.Result = result
	} else {
		return &returnLocal, fmt.Errorf("result nil.")
	}

	returnLocal.Err = err
	return &returnLocal, err
}

// --------------------------------------
//
//	http 调用结束的时候，会请求这个函数
//
// --------------------------------------
func doHttpFinish(returnLocal *ReturnLocal, ignoreResultLog bool) {

	// ---------------------------
	//		崩溃保护
	// ---------------------------
	defer func() {
		if r := recover(); r != nil {
			fmt.Printf("doHttpFinish crash. %+v\n", r)
			debug.PrintStack()
		}
	}()

	if !ignoreResultLog {
		log.Info("[http_request] traceId:%v url:%v postData:%v tcost:%vms rawResult:%v result:%+v err:%v",
			returnLocal.TraceId, returnLocal.Url, returnLocal.PostData, returnLocal.Duration.Milliseconds(), returnLocal.RawResult, returnLocal.Result, returnLocal.Err)
	}

	//parseUrl, err := url.Parse(returnLocal.Url)
	//if err != nil {
	//	logrus.Infof("[parse_url_error] url:%v err:%v", returnLocal.Url, err)
	//	return
	//}

	// -----------------
	// 	请求超时
	//	错误码要强制改为 0
	// -----------------
	if returnLocal.Err != nil && strings.Contains(returnLocal.Err.Error(), "(Client.Timeout exceeded while awaiting headers)") {
		returnLocal.SatusCode = 0
	}

	//if gm.PromeCt.ThirdHttpQpsCount != nil {
	//	gm.PromeCt.ThirdHttpQpsCount.WithLabelValues(parseUrl.Host, parseUrl.Path, m.A2S(returnLocal.SatusCode)).Inc()
	//}

	//if gm.PromeCt.ThirdHttpRequestLatencyHistogram != nil {
	//	gm.PromeCt.ThirdHttpRequestLatencyHistogram.WithLabelValues(parseUrl.Host, parseUrl.Path).Observe(returnLocal.Duration.Seconds())
	//}

	//third_api.SendThirdApiResMsg("", parseUrl.Host, parseUrl.Path, returnLocal.SatusCode, int(returnLocal.Duration.Milliseconds()))
}
